{
 "metadata": {
  "name": ""
 },
 "nbformat": 3,
 "nbformat_minor": 0,
 "worksheets": [
  {
   "cells": [
    {
     "cell_type": "heading",
     "level": 1,
     "metadata": {},
     "source": [
      "Pr\u00e0ctiques de Nous Usos de la Inform\u00e0tica"
     ]
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "Naive Bayes i Classificaci\u00f3"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "<b>ENTREGA: </b>\n",
      "El dia l\u00edmit per a l'entrega d'aquesta pr\u00e0ctica \u00e9s el dia <b>17 de Novembre a les 23.55h</b>\n",
      "\n",
      "<b>Format de l'entrega</b>\n",
      "L'entrega s'efectur\u00e0 mitjan\u00e7ant el campus virtual. S'ha de penjar un arxiu per grup. El nom del fitxer ha de seguir el seg\u00fcent patro:\n",
      "NUI_2_PrimeralletranomCognomMembre1_PrimeralletranomCognomMembre2.iypnb\n",
      "\n",
      "Exemple: <br>\n",
      "Membre 1: Maria del Carme Vil\u00e0<br>\n",
      "Membre 2: Francesc Castell<br>\n",
      "\n",
      "Nom de l'arxiu: <b>NUI_2_MVila_FCastell.ipynb</b>"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "<b>Qu\u00e8 s\u2019ha de fer?</b><br>\n",
      "Volem classificar blogs corresponents a militants de quatre partits pol\u00edtics segons la seva ideologia. Per fer-ho farem servir les seves entrades RSS o Atom. A partir de les seves entrades crearem un vector de caracter\u00edstiques que ens descrigui el contingut del blog. Finalment desenvoluparem un classificador probabil\u00edstic del tipus Naive Bayes que ens permeti identificar la ideologia d'un nou blog donat la seva descripci\u00f3 en les caracter\u00edstiques escollides.<br>\n",
      "\n",
      "<b>Quina \u00e9s la idea del sistema de classificaci\u00f3 que s\u2019ha de desenvolupar?</b><br>\n",
      "El classificador \u00e9s un concepte de l'aprenentatge autom\u00e0tic supervisat. L'objectiu del classificador \u00e9s donat un vector de caracter\u00edstiques que descriuen els objectes que es volen classificar indicar a quina categoria o classe pertanyen d'entre un conjunt predeterminat. El proc\u00e9s de classificaci\u00f3 consta de dues parts: (a) el proc\u00e9s d'aprenentatge i (b) el proc\u00e9s d'explotaci\u00f3 o testeig. El proc\u00e9s d'aprenentatge rep exemples de parelles (x,y) on x s\u00f3n les caracter\u00edstiques, usualment nombres reals, i y \u00e9s la categoria a la que pertanyen. Aquest conjunt se'l coneix com a conjunt d'entrenament i ens servir\u00e0 per trobar una funci\u00f3 y=h(x) que donada una x m'indiqui quina \u00e9s la y. Per altra banda el proc\u00e9s de testeig aplica la funci\u00f3 h(x) apresa a l'entrenament a una nova descripci\u00f3 per veure quina categoria li correspon.</br>\n",
      "\n",
      "<b>Classificaci\u00f3 i llenguatge natural</b><br>\n",
      "La descripci\u00f3 dels exemples en caracter\u00edstiques \u00e9s el punt m\u00e9s cr\u00edtic de tot sistema d'aprenentatge autom\u00e0tic. Una de les representacions m\u00e9s simples per tal de descriure un text \u00e9s la representaci\u00f3 bag-of-words. Aquesta representaci\u00f3 converteix un text en un vector de N paraules. Consisteix en seleccionar un conjunt d'N paraules i per cada paraula contar quants cops apareix en el text. Una versi\u00f3 alternativa d'aquest proc\u00e9s pot ser simplement indicar si apareix o no en el text.\n",
      "\n",
      "<b>Exemple.</b>\n",
      "Imaginem que tenim 4 texts que pertanyen nom\u00e9s a dues categories y={'ERC, 'ICV'}, podr\u00edem seleccionar les seg\u00fcents paraules per tal de distingir les dues categories x={'nuclear', 'verd', 'ecologia', 'independ\u00e8ncia', 'autonomia', 'refer\u00e8ndum'}\n",
      "\n",
      "<table border=\"1\">\n",
      "<tr>\n",
      "<td></td>\n",
      "<td>nuclear</td>\n",
      "<td>verd</td>\n",
      "<td>ecologia</td>\n",
      "<td>independ\u00e8ncia</td>\n",
      "<td>autonomia</td>\n",
      "<td>refer\u00e8ndum</td>\n",
      "</tr>\n",
      "<tr>\n",
      "<td>tesxt 1('ERC')</td>\n",
      "<td>0</td>\n",
      "<td>0</td>\n",
      "<td>0</td>\n",
      "<td>1</td>\n",
      "<td>2</td>\n",
      "<td>3</td>\n",
      "</tr>\n",
      "<tr>\n",
      "<td>tesxt 2('ERC')</td>\n",
      "<td>0</td>\n",
      "<td>1</td>\n",
      "<td>0</td>\n",
      "<td>1</td>\n",
      "<td>2</td>\n",
      "<td>3</td>\n",
      "</tr>\n",
      "<tr>\n",
      "<td>tesxt 1('ICV')</td>\n",
      "<td>1</td>\n",
      "<td>3</td>\n",
      "<td>1</td>\n",
      "<td>0</td>\n",
      "<td>1</td>\n",
      "<td>0</td>\n",
      "</tr>\n",
      "<tr>\n",
      "<td>tesxt 2('ICV')</td>\n",
      "<td>2</td>\n",
      "<td>1</td>\n",
      "<td>2</td>\n",
      "<td>0</td>\n",
      "<td>0</td>\n",
      "<td>0</td>\n",
      "</tr>\n",
      "</table>\n",
      "\n",
      "<br>\n",
      "amb aquesta representaci\u00f3 el text 2, corresponent a la categoria 'ERC', quedaria representat pel vector num\u00e8ric (0,1,0,1,2,3). Si fem servir la representaci\u00f3 alternativa tindr\u00edem (0,1,0,1,1,1) que indica la pres\u00e8ncia de les paraules. Si la descripci\u00f3 \u00e9s adient s'espera que les categories es puguin distingir amb facilitat.\n",
      "<br><br>\n",
      "<b>El classificador Na\u00efve Bayes.</b><br>\n",
      "Un cop tenim una representaci\u00f3 necessitem un proc\u00e9s d'aprenentatge que ens permeti passar de la descripci\u00f3 a una categoria. En aquesta pr\u00e0ctica farem servir el classificador Na\u00efve Bayes. Aquest classificador forma part de la fam\u00edlia de classificadors probabil\u00edstics. La sortida d'un classificador probabil\u00edstic \u00e9s un valor de probabilitat donat un exemple per cadascuna de les categories. La decisi\u00f3 final correspon a la categoria amb m\u00e9s probabilitat. Per exemple, amb la descripci\u00f3 anterior esperem que la sortida sigui de l'estil,<br>\n",
      "$$p( y = 'ERC' | x = (0,1,0,1,1,1)) = 0.6$$\n",
      "$$p( y = 'ICV' | x = (0,1,0,1,1,1)) = 0.4$$\n",
      "\n",
      "<br>\n",
      "Els classificadors probabilistics Bayesians es basen en el teorema de Bayes per realitzar els c\u00e0lculs per trobar la probabilitat condicionada. Es basen en el teorema de Bayes que diu:<br>\n",
      "\n",
      "$$ p(x,y) = p(x|y)p(y) = p(y|x)p(x)$$\n",
      "<br>\n",
      "d'on podem extreure que: <br>\n",
      "$$ p(y,x) = \\frac{p(x|y)p(y)}{p(x)}$$\n",
      "<br>\n",
      "\n",
      "\n",
      "En molts casos p(y) i p(x) s\u00f3n desconeguts i es consideren equiprobables. Per tant, la\n",
      "decisi\u00f3 es simplifica a:\n",
      "<br>\n",
      "$$ p(y|x) = c \u00b7 p(x|y)$$\n",
      "\n",
      "<br>\n",
      "Les deduccions fins a aquest punt s\u00f3n v\u00e0lides per la majoria de classificadors Bayesians. Na\u00efve Bayes es distingeix de la resta perqu\u00e8 imposa una condici\u00f3 encara m\u00e9s restrictiva. Considerem x=(x1,x2,x3,...,xN) un conjunt d'N variables aleat\u00f2ries. Na\u00efve Bayes assumeix que totes elles s\u00f3n independents entre elles i per tant podem escriure:\n",
      "<br>\n",
      "$$p(x_1,x_2,...,x_N | y) = p(x_1|y)p(x_2|y)...p(x_N|y)$$\n",
      "\n",
      "<br>\n",
      "Per tant en el nostre cas es pot veure com:\n",
      "<br>\n",
      "$$p(y='ERC'|x=(0,1,0,1,1,1)) = p(x_1=0|y='ERC')p(x_2=1|y='ERC')...p(x_6=1|y='ERC')$$\n",
      "\n",
      "<br>\n",
      "Podem interpretar l'anterior equaci\u00f3 de la seg\u00fcent forma: La probabilitat de que el document descrit pel vector de caracter\u00edstiques (0,1,0,1,1,1) es proporcional al producte de la probabilitat que la primera paraula del vector no aparegui en els documents de la categoria 'ERC' per la probabilitat que la segona paraula s\u00ed aparegui als documents d''ERC', etc.\n",
      "\n",
      "<br>\n",
      "<b>Estimant les probabilitats marginals condicionades</b>\n",
      "L'\u00faltim pas que ens queda \u00e9s trobar el valor de les probabilitats condicionades. Fem servir la representaci\u00f3 de 0 i 1's indicant que la paraula no apareix (0) o s\u00ed apareix (1) al document. Per trobar el valor de la probabilitat condicionada farem servir una aproximaci\u00f3 freq\u00fcentista a la probabilitat. Aix\u00f2 vol dir que calcularem la freq\u00fc\u00e8ncia d'aparici\u00f3 de cada paraula per a cada categoria. Aquest c\u00e0lcul es fa dividint el nombre de documents de la categoria en que apareix la paraula pel nombre total de documents d'aquella categoria. En l'exemple anterior, p(x2=apareix 'verd'|y='ERC')=1/2 mentres que p(x2=apareix 'verd'|y='ICV')=2/2.\n",
      "<br>\n",
      "$$p(x_2 = 1 | y = 'ERC')= \\frac{A}{B} $$\n",
      "<br>\n",
      "on A \u00e9s el n\u00famero de blocs de 'ERC' on apareix la paraul 'verd' i B \u00e9s el n\u00famero de bloc de 'ERC'.\n",
      "\n",
      "\n",
      "<b>PROBLEMES: </b><br>\n",
      "<b> * El problema de la probabilitat 0</b>\n",
      "Si us fixeu a l'anterior exemple la probabilitat <b>p(x2=no apareix 'verd' | y='ICV')</b> \u00e9s 0 !! Aix\u00f2 vol dir, que si no apareix la paraula 'verd' al blog no pot ser d''ICV' !! No sembla raonable que es penalitzi totalment l'aparici\u00f3 d'aquesta categoria pel fet que una \u00fanica paraula aparegui o no al text. Per tant el que s'acostuma a fer \u00e9s donar una baixa probabilitat en comptes de zero. Una de les possibles solucions es fer servir la correcci\u00f3 de Laplace. Seguint l'exemple anterior la correcci\u00f3 de Laplace \u00e9s\n",
      "\n",
      "<br>\n",
      "$$p(x_2=1 | y = 'ERC' ) = \\frac{A+1}{B+M}$$ \n",
      "on M \u00e9s el nombre de catergories\n",
      "\n",
      "<b> * El problema del 'underflow'</b>\n",
      "L'elecci\u00f3 de les paraules que formen el vector de caracter\u00edstiques \u00e9s un pas cr\u00edtic. En funci\u00f3 de quan bona \u00e9s aquesta descripci\u00f3 millor funcionar\u00e0 el sistema. Tot i que us deixem a vosaltres la pol\u00edtica de creaci\u00f3 del vector de caracter\u00edstiques us donem una d'exemple. Per saber quines paraules fer servir podeu seleccionar de totes les paraules de tots els blogs aquelles que apareixen entre en un 10 i un 50 percent dels blogs (sense tenir en compte la categoria). Podeu experimentar a variar aquests valors."
     ]
    },
    {
     "cell_type": "raw",
     "metadata": {},
     "source": [
      "Els fitxers python feedparser.py i generatefeedvector.py. El fitxer feedparser.py ens permet recuperar la informaci\u00f3 proporcionada per sistemes de feed com RSS o Atom.  \n",
      "El fitxer generatefeedvector.py proporciona utilitats b\u00e0siques per dividir el resultat de l'anterior en paraules eliminant tags d'HTML, traduint car\u00e0cters unicode i HTML corresponents al sistema de puntuaci\u00f3 catal\u00e0. \n",
      "A m\u00e9s trobareu el fitxer blogs.txt que cont\u00e9 una llista de blogs amb el seg\u00fcent format:  ID | Partit | Nom | URL"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "import math\n",
      "import csv\n",
      "import generatefeedvector as generate\n",
      "import pandas as pd\n",
      "import numpy as np\n",
      "import json\n",
      "\n",
      "#Retorna un diccionari amb la seguent estricutra: {id_blog:(nom_politic,{paraula1:numAparicions;paraule2:numAparicision,...})}\n",
      "def download_text(df):\n",
      "    dicc={}\n",
      "    invalid_blogs=[]\n",
      "    #recorremos la lista con toda la informacion de todos los blogs\n",
      "    urls=list(df['url'])\n",
      "    for i in df.index:\n",
      "        try:\n",
      "            dicc[i]=generate.getwordcounts(df.ix[i]['url'])\n",
      "            #print \"blog :\", df.ix[i]['nom']\n",
      "        except:\n",
      "            invalid_blogs.append(df.index[i])\n",
      "            #print \"Link incorrecte, blog\", df.ix[i]['nom']\n",
      "    print \"\\nLectura dels blogs finalizada.\\n\",\n",
      "    return dicc,invalid_blogs\n",
      "        \n",
      "\n",
      "# CREACIO DEL DATAFRAME \n",
      "unames = ['id_blog', 'partit_politic', 'nom', 'url']\n",
      "df = pd.read_table('blogs.dat',sep='::', header=None, names=unames)\n",
      "\n",
      "data_blogs = {}\n",
      "invalid_blogs = []\n",
      "\n",
      "with open('data_blogs.json', 'r') as f:\n",
      "\tdata_blogs = json.load(f)\n",
      "\n",
      "with open('invalid_blogs.json', 'r') as f:\n",
      "\tinvalid_blogs = json.load(f)\n",
      "\n",
      "df=df.drop(invalid_blogs)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 1
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "# CREACIO DEL DATAFRAME \n",
      "unames = ['id_blog', 'partit_politic', 'nom', 'url']\n",
      "df = pd.read_table('blogs.dat',sep='::', header=None, names=unames)\n",
      "\n",
      "# EXEMPLE DE COM BAIXR D'UN CONJUNT DE BLOGS\n",
      "data_blogs,invalid_blogs=download_text(df)\n",
      "# EXEMPLE DE COM ELIMINAR AQUELLS BLOGS QUE NO S'HA POGUT ACCEDIR\n",
      "df=df.drop(invalid_blogs)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": []
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "<b>Exercici 1 </b> Escriure una funci\u00f3 <b>count_blogs(dataframe)</b> que retorni el nombre total de blocs. "
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Retorna el n\u00famero total de blogs. \n",
      "def count_blogs(df):\n",
      "    return len(df.id_blog.unique())\n",
      "\n",
      "count_blogs(df)\n"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "metadata": {},
       "output_type": "pyout",
       "prompt_number": 2,
       "text": [
        "120"
       ]
      }
     ],
     "prompt_number": 2
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "<b>Exercici 2:</b> Escriure una funci\u00f3 <b>count_party_blogs(dataframe)</b> que compte quants blogs hi ha per cadascun dels partits."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Retorna una Series que cont\u00e9 el n\u00famero de blogs per partit pol\u00edtic\n",
      "def count_blogs_party(df):\n",
      "    partits_politics = df.partit_politic.unique()       \n",
      "    series = pd.Series(data = [len(df[df.partit_politic == partit_politic]) for partit_politic in partits_politics],\n",
      "                       index = partits_politics)\n",
      "    return series\n",
      "\n",
      "count_blogs_party(df)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "metadata": {},
       "output_type": "pyout",
       "prompt_number": 3,
       "text": [
        "PSC    34\n",
        "ERC    29\n",
        "CIU    32\n",
        "ICV    25\n",
        "dtype: int64"
       ]
      }
     ],
     "prompt_number": 3
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "<b>Exercici 3:</b> Escriure una funci\u00f3 <b>count_words(dataframe,data_blogs)</b> que que retorni un diccionari amb totes les paraules que han aparescut en els blogs, indicant la quantitat de cops i el nombre de blogs on ha sortit.\n",
      "<br>Possible format sortida: {word :  {freq: valor;n_cops: valor}}"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "# Aquesta funci\u00f3 ha de contruir un diccionari que contingui totes les paraules que s'han trobat indicant \n",
      "# el total de cops que ha aparescut i el nombre de blogs on apareix\n",
      "# Aquesta funci\u00f3 ha de contruir un diccionari que contingui totes les paraules que s'han trobat indicant \n",
      "# el total de cops que ha aparescut i el nombre de blogs on apareix\n",
      "def count_words(df,data_blogs):\n",
      "   data = {}\n",
      "   for blog in data_blogs.values():\n",
      "       # recorremos las palabras del blog\n",
      "       for word in blog[1]:\n",
      "           # Si la palabra no esta en el diccionario, la incluimos\n",
      "           if word not in data:\n",
      "               data[word] = {'freq': 0, 'n_cops': 0}\n",
      "           # sumamos los valores   \n",
      "           data[word]['freq'] += blog[1][word]\n",
      "           data[word]['n_cops'] += 1\n",
      "   return data\n",
      "#count_words(df, data_blogs)            "
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 4
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "<b>Exercici 4:</b> escriure una funci\u00f3 <b>count_words_party(dataframe,dicc_text)</b> que retorna un diccionari que conte el nombre de cops que ha aparescut cada paraula i el n\u00famero de blogs on  ha aparescut. Aquesta informaci\u00f3 ha de ser dividida en els diferents gruos pol\u00edtics.\n",
      "<br>Possible format sortida: {Partit_potilit :  {word :  {freq: valor;n_cops: valor} } } "
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Cuenta la frecuencia de las palabras por un partido determinado\n",
      "def count_words_party(df,dicc_text):\n",
      "    \n",
      "    #: Diccionario a retornar tipo enunciado\n",
      "    diccionari = {}\n",
      "\n",
      "    #: Partidos politicos que hay\n",
      "    partits_politics = df.partit_politic.unique()\n",
      "    \n",
      "    for partit_politic in partits_politics:\n",
      "        \n",
      "        #: Por cada partido politico extraemos todos los indices de los blogs que pertenezcan\n",
      "        # a dicho partido.\n",
      "        idxBlogs_partitPolitic = df[df.partit_politic == partit_politic].index\n",
      "        \n",
      "        #: Diccionario que contendra todas las palabras que aparecen en los blogs de dicho partido\n",
      "        diccionari_paraules = {}\n",
      "        \n",
      "        for idxBlog in idxBlogs_partitPolitic:\n",
      "            #: Extraemos las palabras de un determinado blog\n",
      "            dict_paraules = dicc_text[str(idxBlog)][1]\n",
      "            \n",
      "            for paraula, numAparicions in dict_paraules.items():\n",
      "                #: Comprobamos si la palabra ya esta en el diccionario de palabras del partido politico\n",
      "                if (paraula in diccionari_paraules):\n",
      "                    #Sumamos numero de apariciones e incrementamos en 1 el\n",
      "                    #numero de blogs en el que aparece\n",
      "                    diccionari_paraules[paraula]['freq'] += numAparicions\n",
      "                    diccionari_paraules[paraula]['n_cops'] += 1\n",
      "                else:\n",
      "                    #Incluimos palabra inicializada con el numero de apariciones inicial\n",
      "                    #y que ha aparecido una vez\n",
      "                    diccionari_paraules[paraula] = {'freq':numAparicions,'n_cops':1}\n",
      "                                                     \n",
      "        #Al partido politico le asignamos el diccionario de palabras totales de sus blogs.\n",
      "        diccionari[partit_politic] = diccionari_paraules\n",
      "        \n",
      "    return diccionari\n",
      "\n",
      "#count_words_party(df,data_blogs)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 8
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "<b>Exercici 5:</b> Calcular amb la funci\u00f3 <b>topNword(df,words_partits,N)</b> quines son les N paraules m\u00e9s representatives (les que apareixen amb m\u00e9s freq\u00fc\u00e8ncia) de cadascun dels partits. Retorneu un diccionari amb els seg\u00fcent format: {PSC: llista_top_words_PSC; ERC: llista_top_words_ERC;...}\n",
      "<br>Teniu en compte que tamb\u00e9 haureu de filtrar aquelles paraules que apareixen en la majoria de blogs, aix\u00ed com tamb\u00e9, les que \u00fanicament apareixen en un conjunt molt petit dels blogs.\n"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Cacula les N parules m\u00e9s representativa de cada partit pol\u00edtic. LA sortida ha de \n",
      "# ser un diccionari on tenim tantes entrades com partits politics\n",
      "# el valors de les entrardes ha de ser una llista amb les paraules seleccionades.\n",
      "def topNwords(df,words_partits,N):\n",
      "    #: numero total de blogs\n",
      "    n_blogs = count_blogs(df);\n",
      "    \n",
      "    #: Partidos politicos que hay\n",
      "    partits = df.partit_politic.unique()\n",
      "    \n",
      "    #: numero de blogs per a cada partit\n",
      "    countBlogs_Partit = count_blogs_party(df)\n",
      "    \n",
      "    #: todas las palabras de todos los blogs\n",
      "    totesParaules = count_words(df,data_blogs)\n",
      "    \n",
      "    #: palabras indiferentes que aparecen muy poco o demasiadas veces\n",
      "    paraules_importants = {}\n",
      "    \n",
      "    #Aquellas palabras que aparezcan menos del 10% de 121 blogs, o mas del \n",
      "    #50% de 121 blogs, seran palabras que aparecen en un conjunto muy peque\u00f1o \n",
      "    #de blogs o que aparecen en la mayoria de blogs, respectivamente\n",
      "    for paraula in totesParaules:\n",
      "        if float(totesParaules[paraula]['n_cops']) / n_blogs > 0.1 and \\\n",
      "            float(totesParaules[paraula]['n_cops']) / n_blogs < 0.5:\n",
      "            paraules_importants[paraula] = totesParaules[paraula]['n_cops']\n",
      "    \n",
      "    #: diccionario de partits i dataframe base, luego sera mas facil ordenar\n",
      "    df_columns = ['partit_politic', 'paraula', 'freq', 'n_cops']\n",
      "    \n",
      "    #: top N paraules importants per a cada partit politic\n",
      "    paraules_importants_partits = {}\n",
      "    \n",
      "    # Recorremos el diccionario de paraules partits, y por cada partido, tan solo mantenemos las palabras\n",
      "    # que estan dentro del diccionario de palabras importantes\n",
      "    for partit in partits:\n",
      "        #: extraemos las palabras de ese partido\n",
      "        paraules_partit = words_partits[partit]\n",
      "    \n",
      "        paraules_importants_partits[partit] = sorted([ ( paraules_partit[paraula]['n_cops'], paraula ) \n",
      "                                                   for paraula in paraules_importants \n",
      "                                                   if paraula in paraules_partit ], reverse=True)[:N]\n",
      "    return paraules_importants_partits\n",
      "#topNwords(df,count_words_party(df,data_blogs),20)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 9
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "<b>Exercici 6</b>: Creeu el vector de caracter\u00edstiques necessari per a fer l\u2019entrenament del Na\u00efve Bayes (funci\u00f3 <b>create_features()</b>)."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "# Crea el vector de caracter\u00edsticas necesari per a l'entrenament del classificador Naive Bayes\n",
      "# selected_words: ha de ser el diccionari que obteniu amb la funci\u00f3 topNWords amb les paraules seleccionades a partir de la funci\u00f3 TopNwords\n",
      "# data_blogs : cont\u00e9 el diccionari amb la informaci\u00f3 de cada blog\n",
      "# Rertorna un diccionari que cont\u00e9 un np.array per a cadascun dels blog amb el vector de caracter\u00edstiques corresponent(mireu l'exemple de l'enunciat)\n",
      "\n",
      "\n",
      "def create_features(data_blogs,top_words):\n",
      "    \n",
      "    diccionari = {}\n",
      "    \n",
      "    #: partidos politicos\n",
      "    partits = df.partit_politic.unique()\n",
      "    \n",
      "    #: lista con todas las palabras para el vector caracteristico\n",
      "    values = top_words.values()\n",
      "    all_topWords = list(set([values[i][j][1] for i in range(len(values)) for j in range(len(values[i]))]))\n",
      "\n",
      "    # Iteramos sobre los partidos\n",
      "    for partit in partits:\n",
      "        \n",
      "        #: indices de los blogs del partido\n",
      "        idxBlogs_partit = df[df.partit_politic == partit].index\n",
      "        \n",
      "        for idxBlog in idxBlogs_partit:\n",
      "            \n",
      "            #: texto, vector de todas las palabras del blog\n",
      "            paraules_blog = data_blogs[str(idxBlog)][1].keys()\n",
      "            \n",
      "            #: vector caracteristico inicialmente a 0, y de longitud igual al texto\n",
      "            caracteristic = pd.Series(data = [0] * len(all_topWords), index = all_topWords)\n",
      "            # Por cada palabra del blog miramos si aparece en el conjunto de las palabras importantes \n",
      "            # del partido\n",
      "            for word in all_topWords:\n",
      "                if word in paraules_blog:\n",
      "                    #Si aparece, lo indicamos\n",
      "                    caracteristic[word] = 1\n",
      "            \n",
      "            # Juntamos blog con su vector caracteristico\n",
      "            diccionari[idxBlog] = caracteristic\n",
      "            \n",
      "    return diccionari\n",
      "\n",
      "#words_partits=count_words(df,data_blogs) aixi no\n",
      "N = 20 # Aquest parametre el podem canviar i fer proves per avaluar quin \u00e9s el millor valor\n",
      "words_partits=count_words_party(df,data_blogs)\n",
      "top_words=topNwords(df,words_partits,N)\n",
      "dict_feat_vector = create_features(data_blogs,top_words)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 19
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "<b>Exercici 7</b>: Implementeu la funci\u00f3 d'aprenentatge del classificador Na\u00efve Bayes (funci\u00f3 <b>naive_bayes_learn()</b>).  La funci\u00f3 ha de mostrar el resultat obtingut per pantall\n",
      "<br>\n",
      "<b> * L'error d'entrenament</b>\n",
      "L'error d'entrenament es troba calculant el percentatge d'errors que s'obtenen quan es fa el testeig amb les mateixes dades utilizades per fer entrenament (aprenentatge). Aquest error es un valor molt optimista de com funcionar\u00e0 el clasificador i mai s'ha de prendre com a mesura per comparar clasificadors.\n",
      "<br>"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "\"\"\"\n",
      "naive_bayes\n",
      "\"\"\"\n",
      "def naive_bayes(dict_feat_vector, feature_vector):\n",
      "    \"\"\"\n",
      "        construimos los vectores caracteristicos de cada partido para el entrenador Naive Bayes\n",
      "    \"\"\"\n",
      "    \n",
      "    #: partidos politicos\n",
      "    partits = df.partit_politic.unique()\n",
      "    \n",
      "    #: dataframe de vectors caracteristicos de blogs, lo hacemos traspuesto, filas = blog, columnas = palabra\n",
      "    frame_vectors = pd.DataFrame(dict_feat_vector).T\n",
      "    \n",
      "    #: partidos politicos\n",
      "    dict_calc = {}\n",
      "    #: total de partidos (clases)\n",
      "    len_partits = len(partits)\n",
      "    # por cada parido\n",
      "    for partit in partits:\n",
      "        # indices de los  blogs del partido\n",
      "        indices = df[df.partit_politic == partit].index\n",
      "        #: total blogs partido\n",
      "        len_blogs = len(indices)\n",
      "        #:series de salida para cada partido\n",
      "        partit_serie = pd.Series(data = [0.] * len(frame_vectors.columns), index = frame_vectors.columns )\n",
      "        #: miniframe del partido\n",
      "        partit_frame = frame_vectors.ix[indices]\n",
      "        # por cada palabra\n",
      "        for palabra in frame_vectors.columns:\n",
      "            #: la suma del vector de la palabra\n",
      "            suma = partit_frame[palabra].sum()\n",
      "            #: la probabilidad basada en Laplace Smoothing\n",
      "            probabilidad = (suma + 1.0) / (len_blogs + len_partits)\n",
      "            # asignamos la probabilidad de la palabra en el partido\n",
      "            partit_serie[palabra] = probabilidad\n",
      "    \n",
      "        dict_calc[partit] = partit_serie\n",
      "    \n",
      "    #: dataframe de vectores caracteristicos de partidos, columnas = partidos, filas = palabras\n",
      "    frame_partits_vectors = pd.DataFrame(dict_calc)\n",
      "    \n",
      "    \n",
      "    probabilitats = []    \n",
      "    # por cada partido, que es una columna\n",
      "    for partit in frame_partits_vectors.columns:\n",
      "        prob = 0\n",
      "\n",
      "        #: series del partido \n",
      "        partit_serie = frame_partits_vectors[partit] \n",
      "        \n",
      "        # extraemos del feature_vector los que son == 1 y calculamos su probabilidad\n",
      "        prob += np.log(partit_serie[feature_vector == 1]).sum()\n",
      "\n",
      "        # extraemos del feature_vector los que son != 1 y calculamos su probabilidad\n",
      "        prob += np.log(1 - partit_serie[feature_vector != 1]).sum()\n",
      "\n",
      "        probabilitats.append((prob, partit))                             \n",
      "\n",
      "    return max(probabilitats)[1]\n",
      "\n",
      "def naive_bayes_learn(df,feature_vector = None):\n",
      "    #CONFIGURACIO\n",
      "    #: Aquest parametre el podem canviar i fer proves per avaluar quin \u00e9s el millor valor\n",
      "    N = 20\n",
      "    # :partidos politicos\n",
      "    partits = df['partit_politic'].unique()\n",
      "    #: devuelve {partitPolitic:{paraula:{cantidad de veces que sale en el partido, en cuantos blogs aparece}}}\n",
      "    words_partits = count_words_party(df,data_blogs)\n",
      "    \n",
      "    #: devuelve {partitPolitic:[palabras mas importantes del partido]}\n",
      "    top_words=topNwords(df,words_partits,N)\n",
      "    \n",
      "    #: devuelve {blog:[vectorCaracteristico]}\n",
      "    dict_feat_vector = create_features(data_blogs,top_words)\n",
      "\n",
      "    \n",
      "    \"\"\"\n",
      "        Rutina del entrenador Naive Bayes\n",
      "    \"\"\"\n",
      "    \n",
      "    aciertos = 0\n",
      "    \n",
      "    for partit in partits:\n",
      "        #: indices de los  blogs del partido\n",
      "        indices = df[df.partit_politic == partit].index\n",
      "        \n",
      "        #: numero de aciertos coincidentes al blog\n",
      "        aciertos_partido = len([\n",
      "                                id_blog for id_blog in indices # id de cada blog\n",
      "                                # llamada a la funcion naive_bayes, comprobamos si la salida es la esperada\n",
      "                                if naive_bayes(dict_feat_vector, dict_feat_vector[id_blog]) == partit\n",
      "                            ])\n",
      "        \n",
      "        # acumulamos los aciertos totales\n",
      "        aciertos += aciertos_partido\n",
      "        \n",
      "        print partit, \"encerts:\", aciertos_partido, \"/\",\n",
      "        print \"blogs:\", len(indices), \"/\",\n",
      "        print \"%.2f\" %((float(aciertos_partido)/ float(len(indices))) * 100),\"%\"\n",
      "        \n",
      "    print \n",
      "    print \"Error naive bayes: %.2f \" %( (1 - (float(aciertos) / float(len(df.id_blog.unique()))))*100),\"%\"\n",
      "\n",
      "\n",
      "## EXEMPLE SORTIDA:\n",
      "#PSC encerts: 24 / blogs: 34 / 70.59 %\n",
      "#ICV encerts: 21 / blogs: 26 / 80.77 %\n",
      "#CIU encerts: 29 / blogs: 32 / 90.62 %\n",
      "#ERC encerts: 26 / blogs: 29 / 89.66 %\n",
      "#\n",
      "#Error naive bayes: 17.36 %\n",
      "naive_bayes_learn(df)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": []
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "<b>Exercici 8: </b> Indiqueu l'error de generalitzaci\u00f3 fent servir Leave-one-out (funci\u00f3 <b>leave1out ()</b> )\n",
      "\n",
      "<b> * Aproximaci\u00f3 a l'error de generalitzaci\u00f3 fent servir Leave-one-out</b>\n",
      "Una bona forma de veure com funcionaria el nostre classificador davant de dades sobre les quals no s'ha entrenat \u00e9s fer servir l'estrat\u00e8gia leave-one-out. Aquesta estrat\u00e8gia entrena el classificador amb totes les dades d'entrenament menys amb una i fa el testeig sobre la dada que hem excl\u00f2s de l'entrenament. Aquest proc\u00e9s d'exclusi\u00f3 es repeteix per cadascuna de les dades d'entrenament. El percentatge d'errors fent servir aquesta estrat\u00e8gia permet comparar classificadors."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#M\u00e8tode per avaluar el classificador mitjan\u00e7ant la t\u00e8cnica leave-one-out.  Ha de mostrar el resultat obtingut per pantalla\n",
      "def leave1out(df,feature_vector = None ):\n",
      "    #CONFIGURACIO\n",
      "    #: Aquest parametre el podem canviar i fer proves per avaluar quin \u00e9s el millor valor\n",
      "    N = 50    \n",
      "    #: partidos politicos\n",
      "    partits = df['partit_politic'].unique()\n",
      "    aciertos_partidos = {partit: 0 for partit in partits}\n",
      "    aciertos = 0\n",
      "    \n",
      "    for blog in df.index:\n",
      "\n",
      "        #: devuelve {partitPolitic:{paraula:{cantidad de veces que sale en el partido, en cuantos blogs aparece}}}\n",
      "        words_partits = count_words_party(df,data_blogs)\n",
      "        \n",
      "        #: devuelve {partitPolitic:[palabras mas importantes del partido]}\n",
      "        top_words=topNwords(df,words_partits,N)\n",
      "        \n",
      "        #: devuelve {blog:[vectorCaracteristico]}\n",
      "        dict_feat_vector = create_features(data_blogs,top_words)\n",
      "\n",
      "        #: extraemos el blog incognita\n",
      "        feature_vector = dict_feat_vector.pop(blog)\n",
      "        \n",
      "        \"\"\"\n",
      "            Rutina del entrenador Naive Bayes\n",
      "        \"\"\"\n",
      "        \n",
      "\n",
      "        #: extraemos el partido del blog a examinar\n",
      "        partit = df.ix[blog]['partit_politic']\n",
      "        \n",
      "        calculo = naive_bayes(dict_feat_vector, feature_vector)\n",
      "        if calculo == partit:\n",
      "            aciertos_partidos[partit] += 1\n",
      "            aciertos += 1\n",
      "            \n",
      "            \n",
      "    for partit in partits:\n",
      "        \n",
      "        print partit, \"encerts:\", aciertos_partidos[partit], \"/\",\n",
      "        print \"blogs:\",len(df[df.partit_politic == partit].index), \"/\",\n",
      "        print \"%.2f\" %((float(aciertos_partidos[partit])/ float(len(df[df.partit_politic == partit].index))) * 100),\"%\"\n",
      "        \n",
      "    print \n",
      "    print \"Error naive bayes: %.2f \" %( (1 - (float(aciertos) / float(len(df.index))))*100),\"%\"\n",
      "\n",
      "leave1out(df)\n",
      "    \n",
      "## EXEMPLE SORTIDA:\n",
      "#PSC encerts: 24 / blogs: 34 / 70.59 %\n",
      "#ICV encerts: 21 / blogs: 26 / 80.77 %\n",
      "#CIU encerts: 29 / blogs: 32 / 90.62 %\n",
      "#ERC encerts: 26 / blogs: 29 / 89.66 %\n",
      "#\n",
      "#Error naive bayes: 17.36 %"
     ],
     "language": "python",
     "metadata": {},
     "outputs": []
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "<b>Exercici 9</b> Definici\u00f3 de la funci\u00f3 principal. Modifiqueu la funci\u00f3 per tal que s'ajusti a les vostres funcions."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "# Main. Se hacen las llamadas a las diversas funciones para la correcta ejecucion del programa    \n",
      "def main():\n",
      "    \n",
      "    unames = ['id_blog', 'partit_politic', 'nom', 'url']\n",
      "    df = pd.read_table('blogs.dat',sep='::', header=None, names=unames)\n",
      "    \n",
      "    # EXEMPLE DE COM BAIXR D'UN CONJUNT DE BLOGS\n",
      "    data_blogs,invalid_blogs=download_text(df)\n",
      "    # EXEMPLE DE COM ELIMINAR AQUELLS BLOGS QUE NO S'HA POGUT ACCEDIR\n",
      "    df=df.drop(invalid_blogs)\n",
      "    \n",
      "    # agafem la frequencia de paraules que apareix a cada blog\n",
      "    words_partits=count_words(df,data_blogs)\n",
      "    \n",
      "    #agafem el conjunt de paraules que utilziarem\n",
      "    N = 50 # Aquest parametre el podem canviar i fer proves per avaluar quin \u00e9s el millor valor\n",
      "    top_words=topNwords(df,count_words_party(df,data_blogs),N)\n",
      "    print\n",
      "    print \"---------------------\"\n",
      "    print\n",
      "    print \"Paraules mes representatives dels blogs\"\n",
      "    print\n",
      "    for partit in top_words:\n",
      "        print partit\n",
      "        print top_words[partit]\n",
      "    \n",
      "     # SORTIDA ESPERADA :\n",
      "    #Paraules m\u00e9s representatives mas representativas\n",
      "    #PSC \n",
      "    #[u'los', u'por', u'las', u'para', u'mas', u'psc', u'con', u'est', u'mso', u'tant', u'democr', u'partit', u'pais', u'social', u'nos', u'temps', u'nou', u'como', u'primer', u'sino']\n",
      "    #ICV \n",
      "    #[u'los', u'mso', u'las', u'sobre', u'icv', u'social', u'ciutat', u'para', u'por', u'gent', u'est', u'ciu', u'contra', u'persones', u'font', u'fins', u'qui', u'crisi', u'part', u'any']\n",
      "    #CIU \n",
      "    #[u'ciu', u'ciutat', u'nostre', u'barcelona', u'tant', u'nostra', u'sempre', u'pais', u'mso', u'ajuntament', u'fins', u'gent', u'any', u'aquests', u'gran', u'nou', u'avui', u'poble', u'president', u'est']\n",
      "    #ERC \n",
      "    #[u'pais', u'esquerra', u'sobre', u'social', u'nostre', u'fins', u'espanyol', u'part', u'nostra', u'gent', u'tant', u'persones', u'nacional', u'poble', u'any', u'erc', u'encara', u'esta', u'aquests', u'catalana']\n",
      "    \n",
      "    \n",
      "    \n",
      "    #creem el vector de caracter\u00edstiques\n",
      "    feature_vectors = create_features(data_blogs,top_words)\n",
      "    \n",
      "    #Creem i avaluem el cassificador amb les mateixes dades d'entranament\n",
      "    #el nostre es diu learn_naive_bayes\n",
      "    print\n",
      "    print \"---------------------\"\n",
      "    print\n",
      "    print \"Dades del Naive Bayes\"\n",
      "    naive_bayes_learn(df,feature_vectors) \n",
      "    \n",
      "    \n",
      "    #PSC encerts: 24 / blogs: 34 / 70.59 %\n",
      "    #ICV encerts: 21 / blogs: 26 / 80.77 %\n",
      "    #CIU encerts: 29 / blogs: 32 / 90.62 %\n",
      "    #ERC encerts: 26 / blogs: 29 / 89.66 %\n",
      "    #\n",
      "    #Error naive bayes: 17.36 %\n",
      "    \n",
      "    #Avaluem el classificador mitjan\u00e7ant la t\u00e8cnica de leave-lone-out\n",
      "    print\n",
      "    print \"---------------------\"\n",
      "    print\n",
      "    print \"Dades del leave1out\"\n",
      "    leave1out(df, feature_vectors)\n",
      "    \n",
      "    #PSC encerts: 17 / blogs: 34 / 50.00 %\n",
      "    #ICV encerts: 17 / blogs: 26 / 65.38 %\n",
      "    #CIU encerts: 26 / blogs: 32 / 81.25 %\n",
      "    #ERC encerts: 24 / blogs: 29 / 82.76 %\n",
      "    #\n",
      "    #Error leave one out: 30.58 %\n",
      " \n",
      "\n"
     ],
     "language": "python",
     "metadata": {},
     "outputs": []
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "main()"
     ],
     "language": "python",
     "metadata": {},
     "outputs": []
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [],
     "language": "python",
     "metadata": {},
     "outputs": []
    }
   ],
   "metadata": {}
  }
 ]
}